/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice;

import org.proteusframework.core.api.IFactoryRegistrar;
import org.proteusframework.core.api.IPlatformDelegate;
import org.proteusframework.core.api.IPluginRegistrar;
import org.proteusframework.core.api.IRuntimeEngine;
import org.proteusframework.core.api.config.IConfiguration;
import org.proteusframework.core.api.ext.IAdapterManager;
import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.core.api.model.IPlugin;
import org.proteusframework.core.base.NoSuchNamespaceException;
import org.proteusframework.core.util.Assert;
import org.proteusframework.platformservice.appconfig.IAppConfigService;
import org.proteusframework.platformservice.lifecycle.api.ILifecycleService;
import org.proteusframework.platformservice.messaging.IMessageService;
import org.proteusframework.platformservice.persistence.api.IPersistenceService;
import org.proteusframework.platformservice.theme.IThemeService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.proteusframework.platformservice.ServiceClassification.*;

/**
 * @author Tacoma Four
 */
public class UnitTestDelegate implements IExtendedPlatformDelegate
{
    private ILifecycleService lifecycleService;
    private IAppConfigService appConfig;
    private IPersistenceService persistService;
    private final ServiceScopeManager serviceScopeManager = new ServiceScopeManager();

    private Map<Integer, IPlugin> customServiceMap = new HashMap<Integer, IPlugin>();

    private List<Class<? extends IServicePlugin>> knownCustomServicePlugins = new ArrayList<Class<? extends IServicePlugin>>();

    public UnitTestDelegate(IAppConfigService configService, IPersistenceService persistService, ILifecycleService lifecycleService)
    {
        this.appConfig = configService;
        this.persistService = persistService;
        this.lifecycleService = lifecycleService;
        this.serviceScopeManager.setPlatformDelegate(this, IExtendedPlatformDelegate.class);
    }

    public UnitTestDelegate(IAppConfigService configService, IPersistenceService persistService, ILifecycleService lifecycleService, Class<? extends IServicePlugin>... customServicePlugin)
    {
        this.appConfig = configService;
        this.persistService = persistService;
        this.lifecycleService = lifecycleService;
        this.serviceScopeManager.setPlatformDelegate(this, IExtendedPlatformDelegate.class);

        if (null != customServicePlugin && customServicePlugin.length > 0)
        {
            for (Class<? extends IServicePlugin> plugin : customServicePlugin)
            {
                if (null != plugin)
                {
                    knownCustomServicePlugins.add(plugin);
                }
            }
        }
    }

    @Override
    public <T extends IServicePlugin> T getPlatformService(Class<T> expectedInterface) throws ServiceNotInstalledException
    {
        if (IAppConfigService.class.isAssignableFrom(expectedInterface))
        {
            return getPlatformService(ApplicationConfiguration, expectedInterface);
        } else if (ILifecycleService.class.isAssignableFrom(expectedInterface))
        {
            return getPlatformService(Lifecycle, expectedInterface);
        } else if (IPersistenceService.class.isAssignableFrom(expectedInterface))
        {
            return getPlatformService(Persistence, expectedInterface);
        } else if (IThemeService.class.isAssignableFrom(expectedInterface))
        {
            return getPlatformService(Theme, expectedInterface);
        } else
        {
            return getPlatformService(Custom, expectedInterface);
        }
    }

    @Override
    public IAppConfigService getAppConfigService()
    {
        return appConfig;
    }

    @Override
    public IPersistenceService getPersistenceService()
    {
        return persistService;
    }

    @Override
    public <T extends IServicePlugin> IConfiguration getServiceConfiguration(Class<T> serviceInterface)
    {
        T implementation = getPlatformService(serviceInterface);
        return (IConfiguration) implementation;
    }


    @Override
    public <T extends IServicePlugin> IConfiguration getServiceConfiguration(INamespace namespace, Class<T> serviceInterface) throws ServiceNotInstalledException
    {
        T implementation = getPlatformService(namespace, serviceInterface);
        return (IConfiguration) implementation;
    }

    @Override
    public <T extends IServicePlugin> T getPlatformService(Class<T> expectedInterface, ServiceScope scope) throws ServiceScopingException
    {
        return serviceScopeManager.getPlatformService(expectedInterface, scope);
    }

    @Override
    public <T extends IServicePlugin> T getPlatformService(Class<T> expectedInterface, ServiceScope scope, INamespace namespace) throws ServiceScopingException
    {
        return serviceScopeManager.getPlatformService(expectedInterface, scope, namespace);
    }

    @Override
    public ILifecycleService getLifecycleService()
    {
        return lifecycleService;
    }

    @Override
    public boolean isThemeServiceAvailable()
    {
        return false;
    }

    @Override
    public IThemeService getThemeService()
    {
        throw new UnsupportedOperationException("getThemeService() not supported in the test delegate");
    }

    @Override
    public boolean isPersistenceServiceAvailable()
    {
        return true;
    }

    @Override
    public boolean isMessageServiceAvailable()
    {
        return true;
    }

    @Override
    public IMessageService getMessageService()
    {
        throw new UnsupportedOperationException("getMessageService() not supported in the test delegate");
    }

    @Override
    public <T extends IRuntimeEngine> T getEngine(Class<T> engineClassType)
    {
        throw new UnsupportedOperationException("getEngine() not supported in the test delegate");
    }

    @Override
    public <T extends IPlatformDelegate> T adapt(Class<T> delegateClassType)
    {
        T targetDelegate = null;

        if (delegateClassType.isAssignableFrom(this.getClass()))
        {
            targetDelegate = delegateClassType.cast(this);
        } else
        {
            throw new IllegalStateException("Application's IPlatformDelegate is not assignable to class " +
                    delegateClassType.getName());
        }

        return targetDelegate;
    }

    @Override
    public <T extends IServicePlugin> T getPlatformService(ServiceClassification serviceClassification, Class<T> serviceInterface) throws ServiceNotInstalledException
    {
        T result = null;

        switch (serviceClassification)
        {
            case Lifecycle:
                result = serviceInterface.cast(lifecycleService);
                break;
            case Persistence:
                result = serviceInterface.cast(persistService);
                break;
            case Messaging:
//                result = serviceInterface.cast(messageService);
                throw new ServiceNotInstalledException(serviceClassification, IMessageService.class);
            case Theme:
                throw new ServiceNotInstalledException(serviceClassification, IThemeService.class);
            case ApplicationConfiguration:
                result = serviceInterface.cast(appConfig);
                break;
            case Custom:
                Integer serviceIntfHashCode = serviceInterface.hashCode();

                if (customServiceMap.containsKey(serviceIntfHashCode))
                {
                    result = serviceInterface.cast(customServiceMap.get(serviceIntfHashCode));
                } else
                {
                    // first request
                    try
                    {
                        //noinspection unchecked
                        result = (T) fetchCustomService(serviceInterface);
                    } catch (ServiceNotInstalledException e)
                    {
                        this.getEngine(IExtendedRuntimeEngine.class)
                                .onCustomPlatformServiceInitializationException(new ServiceInitializationException(e.getMessage(), e));
                    }
                }
                break;
        }

        if (null == result)
        {
            throw new ServiceNotInstalledException(serviceClassification, serviceInterface);
        }

        return result;
    }

    @SuppressWarnings({"unchecked", "SuspiciousMethodCalls"})
    private <T extends IPlugin> T fetchCustomService(Class<T> serviceInterface)
    {
        Assert.parameterNotNull(serviceInterface, "Parameter 'serviceInterface' must not be null");

        Integer customServiceHashCode = serviceInterface.hashCode();

        IPlugin pluginInstance = null;

        if (customServiceMap.containsKey(customServiceHashCode))
        {
            pluginInstance = customServiceMap.get(customServiceHashCode);
        } else
        {
            if (knownCustomServicePlugins.contains(serviceInterface))
            {
                try
                {
                    pluginInstance = serviceInterface.newInstance();
                    pluginInstance.setPlatformDelegate(this, IExtendedPlatformDelegate.class);
                    ((IServicePlugin) pluginInstance).start();
                    customServiceMap.put(customServiceHashCode, pluginInstance);
                } catch (InstantiationException e)
                {
                    e.printStackTrace();
                } catch (IllegalAccessException e)
                {
                    e.printStackTrace();
                }
            } else
            {
                throw new ServiceNotInstalledException(ServiceClassification.Custom, serviceInterface);
            }
        }

        return (T) pluginInstance;
    }

    @Override
    public <T extends IServicePlugin> T getPlatformService(INamespace namespace, Class<T> expectedInterface) throws ServiceNotInstalledException
    {
        throw new UnsupportedOperationException("getPlatformService() not supported in the test delegate");
    }

    @Override
    public IFactoryRegistrar getFactoryRegistrar()
    {
        throw new UnsupportedOperationException("getFactoryRegistrar() not supported in the test delegate");
    }


    @Override
    public IPluginRegistrar getPluginRegistrar(INamespace namespace) throws NoSuchNamespaceException
    {
        throw new UnsupportedOperationException("getPluginRegistrar() not supported in the test delegate");
    }

    @Override
    public IAdapterManager getAdapterManager()
    {
        throw new UnsupportedOperationException("getAdapterManager() not supported in the test delegate");
    }

}
